package com.citydo.webrtcspringboot.websocket.stream;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.citydo.webrtcspringboot.entity.Message;

import javax.websocket.Session;
import java.util.*;
import java.util.List;

public class StreamManager {
    //每个推流端可连接的客户端的最大数量
    private static final int MAX_CLIENT = 2;
    //最大的独享推流端数量
    private static final int MAX_TWITCH_UNSHARED = 1;
    //最大的独享web端的数量
    private static final int MAX_WEB_UNSHARED = 2;
    private static StreamManager _instance = null;
    //所有推流端
    private final Map<Session, String> web = new HashMap<>();
    //所有客户端
    private final Map<Session, String> twitch = new HashMap<>();
    //所有连接
    private final Map<Session,String> connect = new HashMap<>();
    //所有客户端的结构信息
    private final Map<String, ConnectStateWeb> webKey = new HashMap<>();
    //所有推流端的结构信息
    private final Map<String, ConnectStateTwitch> twitchKey = new HashMap<>();
    //所有的端的结构信息
    private final Map<String, ConnectState> connectKey = new HashMap<>();
    //配对的connectionID
    private final Map<String, List<String>> pairs = new HashMap<>();
    //独享推流端
    private final Map<String,ConnectStateTwitch> unSharedTwitch = new HashMap<>();
    //共享推流端
    private final Map<String,ConnectStateTwitch> sharedTwitch = new HashMap<>();

    //单例
    public static StreamManager Instance(){
        if(_instance==null){
            _instance = new StreamManager();
        }
        return StreamManager._instance;
    }

    public void PairsPut(String from,String to){
        ConnectState connectState = getConnectKey().get(from);
        if(Objects.equals(connectState.getIdentity(), Message.CONNECTER_TWITCH)) {
            if (pairs.containsKey(from)) {
                getPairs().get(from).add(to);
            }
        }
    }

    //遍历所有twitch
    public void findAllTwitch(){
        getTwitchKey().forEach((key, value) -> {
            System.out.println("Twitch Stream connectionID:" + key);
        });
    }
    //遍历所有web
    public void findAllWeb(){
        getWebKey().forEach((key, value) -> {
            System.out.println("Web Stream connectionID:" + key);
        });
    }
    //遍历所有connect
    public void findAllConnect(){
        getConnectKey().forEach((key, value) -> {
            System.out.println("Connect Stream connectionID:" + key);
        });
    }

//    public Session getMateSession(String connectionID){
//        if(connectionID==null) {
//            return null;
//        }
//        ConnectState connectState = getConnectKey().get(connectionID);
//        if(connectState==null)
//            return null;
//        String mateConnectionID = connectState.getMateConnectionId();
//        return mateConnectionID==null?null:
//                getConnectKey().get(mateConnectionID).getSession();
//    }

    //当有一个服务进来时，向管理器中添加
    public void Put(String identity,String from,Session session,UserGrade userGrade){
        ConnectState connectState = new ConnectState(from,session,identity);
        getConnectKey().put(from,connectState);
        getConnect().put(session,from);
        getPairs().put(from,new ArrayList<>());
        //进来的服务是推流端时
        if(Objects.equals(identity, Message.CONNECTER_TWITCH)){
            getTwitchKey().put(from,new ConnectStateTwitch(connectState));
            getTwitch().put(session,from);
            if(getUnSharedTwitch().size()<MAX_TWITCH_UNSHARED){
                //独享服务器
                connectState.setUserGrade(UserGrade.UnSharedGroup);
                getUnSharedTwitch().put(from,new ConnectStateTwitch(connectState));
            }else{
                //共享服务器
                connectState.setUserGrade(UserGrade.SharedGroup);
                getSharedTwitch().put(from,new ConnectStateTwitch(connectState));
            }
        }else{
            ConnectStateWeb connectStateWeb = new ConnectStateWeb(connectState);
            connectStateWeb.setUserGrade(userGrade);
            //进来的服务是客户端时
            getWebKey().put(from,connectStateWeb);
            getWeb().put(session,from);
        }
    }
    //选取服务器策略
    public ConnectStateTwitch chooseServer(String from){
        ConnectStateTwitch serverConnectState = new ConnectStateTwitch();
        //独享推流端。
        SortedMap<Integer,ConnectStateTwitch> unSharedSortedMap = new TreeMap<>();
        //共享推流端
        SortedMap<Integer,ConnectStateTwitch> sharedSortedMap = new TreeMap<>();
        for(Map.Entry<String,ConnectStateTwitch> entry : getTwitchKey().entrySet()){
            //只有处于非繁忙状态的推流端才会进入筛选
            if(!entry.getValue().getConnectionState()) {
                if(entry.getValue().getUserGrade()==UserGrade.UnSharedGroup)
                    unSharedSortedMap.put(entry.getValue().getConnectNum(), entry.getValue());
                else
                    sharedSortedMap.put(entry.getValue().getConnectNum(), entry.getValue());
            }
        }

        //若为独享用户，优先选择独享推流服务
        if(getWebKey().get(from).getUserGrade()==UserGrade.UnSharedGroup){
            if(unSharedSortedMap.size()<1) {
                System.out.println("没有专享推流服务器,开始寻找共享服务器...");
                //如果有独享推流服务器则优先选择独享推流服务器，没有则选择共享服务器
                if(sharedSortedMap.size()>0)
                    serverConnectState = sharedSortedMap.get(sharedSortedMap.firstKey());
                else {
                    if(getTwitchKey().size()<1)
                        System.out.println("没有找到服务器~");
                    else
                        System.out.println("没有找到空闲中服务器~");
                }
            }else
                serverConnectState = unSharedSortedMap.get(unSharedSortedMap.firstKey());
        }
        else{
            //若为共享用户，优先选择共享推流服务
            if(sharedSortedMap.size()>=1)
                serverConnectState = sharedSortedMap.get(sharedSortedMap.firstKey());
            else
                System.out.println("没有找到共享服务器~");
        }
        return serverConnectState;
    }
    //配对方法
    public ConnectStateTwitch mateConnection(String from,String userName){
        if(getTwitchKey().size()<1){
            System.out.println("twitch's number is zero");
            return null;
        }
        ActionManager.Instance().addEvent(ActionManager.FuncName.chooseServer,
                params -> chooseServer((String)params[0]));
        ConnectStateWeb clientConnectState = getWebKey().get(from);
        ConnectStateTwitch serverConnectState =
                (ConnectStateTwitch) ActionManager.Instance().invoke(ActionManager.FuncName.chooseServer,from);
        if(serverConnectState!=null) {
            //推流端id
            String serverConnectId = serverConnectState.getConnectionId();
            //设置推流端的mateConnection
            serverConnectState.getMateConnectionId().add(from);
            //设置客户端的mateConnection
            clientConnectState.setMateConnectionId(serverConnectId);
            PairsPut(serverConnectState.getConnectionId(),clientConnectState.getConnectionId());
//            System.out.println("serverConnectID:"+serverConnectID+" webConnectionID:"+connectionID);
            clientConnectState.setConnectNum(1);
            serverConnectState.setConnectNum(serverConnectState.getConnectNum()+1);
            System.out.println("mate success! web id:"+clientConnectState.
                    getConnectionId()+" twitch id:"+serverConnectState.getConnectionId());
            return serverConnectState;
        }else{
            System.out.println("no mated server");
            return null;
        }
    }

    //主动关闭客户端
    public void closeWeb(String from){
        if(!getWebKey().containsKey(from)) return;
        ConnectStateWeb web = getWebKey().get(from);
        JSONObject webJson = new JSONObject();
        webJson.put("connectionId",from);
        webJson.put("type","disconnect");
        StreamManager.sendJson(web.getSession(),webJson.toJSONString());
//        disConnectionWeb(from);
    }
    //客户端解除连接
    public void disConnectionWeb(String from){
        ConnectStateWeb webConnect = getWebKey().get(from);
        if(webConnect==null)
            return;
        String webConnectionId = webConnect.getConnectionId();
        getConnect().remove(getConnectKey().get(from).getSession());
        getWeb().remove(getConnectKey().get(from).getSession());
        getConnectKey().remove(webConnectionId);
        getWebKey().remove(webConnectionId);
        if(webConnect.getMateConnectionId()==null)
            return;
        ConnectStateTwitch connectTwitch = getTwitchKey().get(webConnect.getMateConnectionId());
        if(connectTwitch==null) return;
        List<String> mateIdList = connectTwitch.getMateConnectionId();
        int index = 0;
        for(int i = 0;i<mateIdList.size();i++){
            if(mateIdList.get(i).equals(from)){
                index = i;
            }
        }
        mateIdList.remove(index);
        connectTwitch.setConnectNum(connectTwitch.getConnectNum()-1);
        ConnectStateTwitch server = StreamManager.Instance().getTwitchKey().get(webConnect.getMateConnectionId());
        JSONObject disconnectJson = new JSONObject();
        disconnectJson.put("connectionId", from);
        disconnectJson.put("type", "disconnect");
        StreamManager.sendJson(server.getSession(), disconnectJson.toJSONString());
        System.out.println("移除客户端:"+" webConnectionId:"+webConnectionId+" from:"+from+" getWebKey.size:"+ getWebKey().size());
    }
    //推流端关闭
    public void closeTwitch(String from){
        if(getConnectKey().size()<1)
            return;
        getConnect().remove(getConnectKey().get(from).getSession());
        getTwitch().remove(getConnectKey().get(from).getSession());
        getConnectKey().remove(from);
        getTwitchKey().remove(from);
        getPairs().remove(from);
        /*for(String web : webList){
            webJson.put("connectionId",web);
            webJson.put("type","disconnect");
//            StreamManager.sendJson(getConnectKey().get(web).getSession(),webJson.toJSONString());
            webJson.clear();
            getWeb().remove(getConnectKey().get(web).getSession());
            getConnect().remove(getConnectKey().get(web).getSession());
            getConnectKey().remove(web);
            getWebKey().remove(web);
        }*/
        System.out.println("twitch.size:"+getTwitchKey().size());
    }
    //依据身份关闭对应流
    public void CloseStreamByIdentity(String from,String identity){
        if(Objects.equals(identity,Message.CONNECTER_TWITCH)){
            closeTwitch(from);
        }else{
            disConnectionWeb(from);
        }
    }
    //推流端关闭
/*    public void disMateConnection(String from){
        if(getConnectKey().size()<1){
            System.out.println("no connect disMate");
            return;
        }
        ConnectState connectState = getConnectKey().get(from);
        String identity = connectState.getIdentity();
        String mateConnectId = getConnectKey().get(from).getMateConnectionId();
        Session mateSession = getConnectKey().get(mateConnectId).getSession();
        if(Objects.equals(identity,Message.CONNECTER_TWITCH)){
            connectState.setConnectNum(connectState.getConnectNum()-1);
            connectState.setMateConnectionId(null);
            getConnect().remove(mateSession);
            getWeb().remove(mateSession);
            getWebKey().remove(mateConnectId);
        }
        connectState.setMateConnectionId(null);
    }*/

    public static void sendJson(Session session, String msg) {
        if ((session == null)||!session.isOpen()||(msg == null)) {
            return;
        }
        try {
            JSONObject obj = JSON.parseObject(msg);
            session.getBasicRemote().sendText(JSON.toJSONString(obj));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //通过ID拿到对应配对ID的session
    public Session getOppositeKeyByPair(Map<String,Session> Key,String connectionID){
        return Key.get(connectionID);
    }

    public StreamManager(){
    }

    public Map<String, ConnectStateWeb> getWebKey() {
        return webKey;
    }

    public Map<String, ConnectStateTwitch> getTwitchKey() {
        return twitchKey;
    }

    public Map<String,List<String>> getPairs(){
        return pairs;
    }

    public Map<Session, String> getWeb() {
        return web;
    }

    public Map<Session, String> getTwitch() {
        return twitch;
    }

    public Map<String, ConnectState> getConnectKey() {
        return connectKey;
    }

    public Map<Session, String> getConnect() {
        return connect;
    }

    public Map<String, ConnectStateTwitch> getUnSharedTwitch() {
        return unSharedTwitch;
    }

    public Map<String, ConnectStateTwitch> getSharedTwitch() {
        return sharedTwitch;
    }
}
